/**
 * https://github.com/01org/appframework/blob/master/appframework.js
 */
define('m/query', function(require, exports, module) {

  var __utils = require('m/utils');
  var __platform = require('m/platform');

  /**
   *  This is our master mQuery object that everything is built upon.
   * $ is a pointer to this object
   * @title appframework
   * @param {Window} window The global window object
   * @api private
   */
  var mQuery = (function(window) {
    var nundefined,
      document = window.document,
      emptyArray = [],
      slice = emptyArray.slice,
      classCache = {},
      _jsonPID = 1,
      fragmentRE = /<(\w+)[^>]*>/,
      _attrCache = {},
      _propCache = {},
      /**
       * CSS Properties that can be expressed as a number (w/o 'px')
       * @type {Object}
       */
      cssNumber = {
        'columncount': true,
        'fontweight': true,
        'lineheight': true,
        'column-count': true,
        'font-weight': true,
        'line-height': true,
        'opacity': true,
        'orphans': true,
        'widows': true,
        'zIndex': true,
        'z-index': true,
        'zoom': true
      };

    /**
     * internal function used for M$().css - checks to see if it is a number and the css property
     * needs 'px' added to it
     * @param {string} prop
     * @param {string|number} val
     * @return {boolean}
     * @api private
     */
    function _addPx(prop, val) {
      return (typeof(val) === 'number') && !cssNumber[prop.toLowerCase()] ? val + 'px' : val;
    }

    /**
     * internal function to use domfragments for insertion
     *
     * @param {$mQuery} mQuery An appframework object
     * @param {Element} container
     * @param {boolean=} insert Default: false (append)
     * @api private
     */
    function _insertFragments(mQuery, container, insert) {
      var frag = document.createDocumentFragment();
      if (insert) {
        for (var j = mQuery.length - 1; j >= 0; j--) {
          frag.insertBefore(mQuery[j], frag.firstChild);
        }
        container.insertBefore(frag, container.firstChild);

      } else {

        for (var k = 0; k < mQuery.length; k++) {
          frag.appendChild(mQuery[k]);
        }
        container.appendChild(frag);
      }
      frag = null;
    }

    /**
     * Internal function to test if a class name fits in a regular expression
     * @param {String} name to search against
     * @return {Boolean}
     * @api private
     */
    function classRE(name) {
      return name in classCache ? classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)'));
    }

    /**
     * Internal function that returns a array of unique elements
     * @param {Array} arr array to compare against
     * @return {Array} array of unique elements
     * @api private
     */
    function unique(arr) {
      for (var i = 0; i < arr.length; i++) {
        if (arr.indexOf(arr[i]) !== i) {
          arr.splice(i, 1);
          i--;
        }
      }
      return arr;
    }

    /**
     * Given a set of nodes, it returns them as an array.  Used to find
     * siblings of an element
     * @param {Nodelist} nodes Node list to search
     * @param {Object=} element [optional] objects to find siblings off of
     * @return {Array} array of siblings
     * @api private
     */
    function siblings(nodes, element) {
      var elems = [];
      if (nodes == nundefined) {
        return elems;
      }

      for (; nodes; nodes = nodes.nextSibling) {
        if (nodes.nodeType === 1 && nodes !== element) {
          elems.push(nodes);
        }
      }
      return elems;
    }

    /**
     * This is the internal appframework object that gets extended and added on to it
     * This is also the start of our query selector engine
     * @param {String|Element|Object|Array} toSelect selector
     * @param {String|Element|Object|undefined} what Context
     * @typedef {Object} $mQuery
     * @api private
     */
    var $mQuery = function(toSelect, what) {
      this.length = 0;
      if (!toSelect) {
        return this;
      } else if (toSelect instanceof $mQuery && what == nundefined) {
        return toSelect;
      } else if (__utils.isFunction(toSelect)) {
        return mQuery(document).ready(toSelect);
      } else if (__utils.isArray(toSelect) && toSelect.length != nundefined) { //Passing in an array or object
        for (var i = 0; i < toSelect.length; i++) {
          this[this.length++] = toSelect[i];
        }
        return this;
      } else if (__utils.isObject(toSelect) && __utils.isObject(what)) { //var tmp=$('span');  $('p').find(tmp);
        if (toSelect.length == nundefined) {
          if (toSelect.parentNode == what) {
            this[this.length++] = toSelect;
          }
        } else {
          for (var j = 0; j < toSelect.length; j++) {
            if (toSelect[j].parentNode == what) {
              this[this.length++] = toSelect[j];
            }
          }
        }
        return this;
      } else if (__utils.isObject(toSelect) && what == nundefined) { //Single object
        this[this.length++] = toSelect;
        return this;
      } else if (what !== nundefined) {
        if (what instanceof $mQuery) {
          return what.find(toSelect);
        }

      } else {
        what = document;
      }

      return this.selector(toSelect, what);

    };

    /**
     * This calls the $mQuery function
     * @title M$()
     * @param {(string|Element|Object)=} selector
     * @param {(string|Element|Object)=} what Context
     * @return {$mQuery} an appframework object
     */
    var $ = function(selector, what) {
      return new $mQuery(selector, what);
    };

    /**
     * this is the engine for 'all' and is only exposed internally
     * @param {string|Element|Object|Array} selector
     * @param {string|Element|Object|undefined} what Context
     * @return {$mQuery} an appframework object
     * @api private
     */
    function _selectorAll(selector, what) {
      try {
        return what.querySelectorAll(selector);

      } catch (e) {
        return [];
      }
    }

    /**
     * this is the query selector engine for elements
     * @param {String} selector
     * @param {String|Element|Object|undefined} what [optional] Context
     * @api private
     */
    function _selector(selector, what) {
      /*jshint validthis:true*/

      selector = selector.trim();

      if (selector[0] === '#' && selector.indexOf('.') === -1 && selector.indexOf(',') === -1 && selector.indexOf(' ') === -1 && selector.indexOf('>') === -1) {
        if (what === document)
          _shimNodes(what.getElementById(selector.replace('#', '')), this);
        else
          _shimNodes(_selectorAll(selector, what), this);
      } else if ((selector[0] === '<' && selector[selector.length - 1] === '>') || (selector.indexOf('<') !== -1 && selector.indexOf('>') !== -1)) //html

      {
        var tmp = document.createElement('div');
        if (__platform.isWin8()) {
          MSApp.execUnsafeLocalFunction(function() {
            tmp.innerHTML = selector.trim();
          });
        } else
          tmp.innerHTML = selector.trim();
        _shimNodes(tmp.childNodes, this);
      } else {
        _shimNodes((_selectorAll(selector, what)), this);
      }
      return this;
    }

    function _shimNodes(nodes, obj) {
      if (!nodes) {
        return;
      }
      if (nodes.nodeType) {
        obj[obj.length++] = nodes;
        return;
      }
      for (var i = 0, iz = nodes.length; i < iz; i++) {
        obj[obj.length++] = nodes[i];
      }
    }

    /**
    * Checks to see if the parameter is a $mQuery object
        ```
        var foo=$('#header');
        $.isM$(foo);
        ```
    * @param {*} obj
    * @return {boolean}
    * @title $.isM$(param)
    */
    $.isM$ = function(obj) {
      return (obj instanceof $mQuery);
    };

    /**
    * Iterates through elements and executes a callback.  Returns if false
    ```
    $.each([1,2],function(ind){console.log(ind);});
    ```
    * @param {Array|Object} elements
    * @param {Function} callback
    * @return {Array} elements
    * @title $.each(elements,callback)
    */
    $.each = function(elements, callback) {
      var i, key;
      if (__utils.isArray(elements)) {
        for (i = 0; i < elements.length; i++) {
          if (callback(i, elements[i]) === false) {
            return elements;
          }
        }
      } else if (__utils.isObject(elements)) {
        for (key in elements) {
          if (!elements.hasOwnProperty(key) || key === 'length') {
            continue;
          }
          if (callback(key, elements[key]) === false) {
            return elements;
          }
        }
      }
      return elements;
    };

    /**
     * Prototype for mQuery object.  Also extends $.fn
     */
    $.fn = $mQuery.prototype = {
      namespace: 'mQuery',
      constructor: $mQuery,
      forEach: emptyArray.forEach,
      reduce: emptyArray.reduce,
      push: emptyArray.push,
      indexOf: emptyArray.indexOf,
      concat: emptyArray.concat,
      selector: _selector,
      oldElement: undefined,
      sort: emptyArray.sort,
      slice: emptyArray.slice,
      length: 0,
      /**
       * This is a utility function for .end()
       * @param {Object} params
       * @return {Object} an appframework with params.oldElement set to this
       * @api private
       */
      setupOld: function(params) {
        if (params == nundefined) {
          return M$();
        }
        params.oldElement = this;
        return params;

      },
      /**
      * This is a wrapper to $.map on the selected elements
          ```
          M$().map(function(){this.value+=ind});
          ```
      * @param {Function} fn callback
      * @return {$mQuery} an appframework object
      * @title M$().map(function)
      */
      map: function(fn) {
        var value, values = [],
          i;
        for (i = 0; i < this.length; i++) {
          value = fn.apply(this[i], [i, this[i]]);
          if (value !== nundefined) {
            values.push(value);
          }
        }
        return $(values);
      },
      /**
      * Iterates through all elements and applys a callback function
          ```
          M$().each(function(){console.log(this.value)});
          ```
      * @param {Function} callback
      * @return {Object} an appframework object
      * @title M$().each(function)
      */
      each: function(callback) {
        this.forEach(function(el, idx) {
          callback.call(el, idx, el);
        });
        return this;
      },
      /**
      * This is executed when DOMContentLoaded happens, or after if you've registered for it.
          ```
          $(document).ready(function(){console.log('I'm ready');});
          ```
      * @param {Function} callback
      * @return {Object} an appframework object
      * @title M$().ready(function)
      */

      ready: function(callback) {
        
        if (document.readyState === 'complete' || document.readyState === 'loaded' || (!__platform.isIE() && document.readyState === 'interactive')) { //IE10 fires interactive too early
          callback();
        } else {
          document.addEventListener('DOMContentLoaded', callback, false);
        }
        return this;
      },

      /**
      * Searches through the collection and reduces them to elements that match the selector
          ```
          $('#foo').find('.bar');
          $('#foo').find($('.bar'));
          $('#foo').find($('.bar').get(0));
          ```
      * @param {String|Object|Array} sel Selector
      * @return {Object} an appframework object filtered
      *
      * @title M$().find(selector)
      */
      find: function(sel) {
        if (this.length === 0) {
          return this;
        }
        var elems = [];
        var tmpElems;
        for (var i = 0; i < this.length; i++) {
          tmpElems = ($(sel, this[i]));

          for (var j = 0; j < tmpElems.length; j++) {
            elems.push(tmpElems[j]);
          }
        }
        return $(unique(elems));
      },

      /**
      * Gets or sets the innerHTML for the collection.
      * If used as a get, the first elements innerHTML is returned
          ```
          $('#foo').html(); //gets the first elements html
          $('#foo').html('new html');//sets the html
          $('#foo').html('new html',false); //Do not do memory management cleanup
          ```
      * @param {String} html to set
      * @param {Bool} [cleanup] - set to false for performance tests and if you do not want to execute memory management cleanup
      * @return {$mQuery} an appframework object
      * @title M$().html([html])
      */
      html: function(html, cleanup) {
        var msFix = function() {
          item.innerHTML = html;
        };
        if (this.length === 0) {
          return this;
        }
        if (html === nundefined) {
          return this[0].innerHTML;
        }

        for (var i = 0; i < this.length; i++) {
          if (cleanup !== false) {
            $.cleanUpContent(this[i], false, true);
          }
          if (__platform.isWin8()) {
            var item = this[i];
            MSApp.execUnsafeLocalFunction(msFix);
          } else {
            this[i].innerHTML = html;
          }
        }
        return this;
      },

      /**
      * Gets or sets the innerText for the collection.
      * If used as a get, the first elements innerText is returned
          ```
          $('#foo').text(); //gets the first elements text;
          $('#foo').text('new text'); //sets the text
          ```
      * @param {String} text to set
      * @return {$mQuery} an appframework object
      * @title M$().text([text])
      */
      text: function(text) {
        if (this.length === 0) {
          return this;
        }
        if (text === nundefined) {
          return this[0].textContent;
        }
        for (var i = 0; i < this.length; i++) {
          this[i].textContent = text;
        }
        return this;
      },
      /**
      * Gets or sets a css property for the collection
      * If used as a get, the first elements css property is returned
      * This will add px to properties that need it.
          ```
          M$().css('background'); // Gets the first elements background
          M$().css('background','red')  //Sets the elements background to red
          ```
      * @param {String} attribute The attribute to get
      * @param {String} value Value to set as
      * @param {Element=} obj
      * @return {Object} obj An appframework object
      * @title M$().css(attribute,[value])
      */
      css: function(attribute, value, obj) {
        var toAct = obj != nundefined ? obj : this[0];
        if (this.length === 0) {
          return this;
        }
        if (value == nundefined && typeof(attribute) === 'string') {
          return toAct.style[attribute] ? toAct.style[attribute] : window.getComputedStyle(toAct)[attribute];
        }
        for (var i = 0; i < this.length; i++) {
          if (__utils.isObject(attribute)) {
            for (var j in attribute) {
              this[i].style[j] = _addPx(j, attribute[j]);
            }
          } else {
            this[i].style[attribute] = _addPx(attribute, value);
          }
        }
        return this;
      },

      /**
      * Gets or sets css vendor specific css properties
      * If used as a get, the first elements css property is returned
          ```
          M$().vendorCss('transform'); // Gets the first elements background
          M$().vendorCss('transform','Translate3d(0,40,0)')  //Sets the elements background to red
          ```
      * @param {string} attribute The attribute to get
      * @param {string} value Value to set as
      * @param {Element} obj Value to set as
      * @return {$mQuery} An appframework object
      * @title M$().vendorCss(attribute,[value])
      */
      vendorCss: function(attribute, value, obj) {
        return this.css($.feat.cssPrefix + attribute, value, obj);
      },

      /**
       * Performs a css vendor specific transform:translate operation on the collection.
          ```
          $('#main').cssTranslate('200px,0,0');
          ```
       * @param {string} val Transform values
       * @return {Object} an appframework object
       * @title M$().cssTranslate(value)
       */
      cssTranslate: function(val) {
        return this.vendorCss('Transform', 'translate' + $.feat.cssTransformStart + val + $.feat.cssTransformEnd);
      },

      /**
       * Gets the computed style of CSS values
          ```
         $('#main').computedStyle('display');
          ```
       * @param {String} val CSS property
       * @return {number|string} CSS value
       * @title M$().computedStyle()
       */
      computedStyle: function(val) {
        if (this.length === 0 || val == nundefined) {
          return;
        }
        return window.getComputedStyle(this[0], '')[val];
      },

      /**
      * Sets the innerHTML of all elements to an empty string
          ```
          M$().empty();
          ```
      * @return {Object} an appframework object
      * @title M$().empty()
      */
      empty: function() {
        for (var i = 0; i < this.length; i++) {
          $.cleanUpContent(this[i], false, true);
          this[i].textContent = '';
        }
        return this;
      },

      /**
      * Sets the elements display property to 'none'.
      * This will also store the old property into an attribute for hide
          ```
          M$().hide();
          ```
      * @return {Object} an appframework object
      * @title M$().hide()
      */
      hide: function() {
        if (this.length === 0) {
          return this;
        }
        for (var i = 0; i < this.length; i++) {
          if (this.css('display', null, this[i]) !== 'none') {
            this[i].setAttribute('mQueryOldStyle', this.css('display', null, this[i]));
            this[i].style.display = 'none';
          }
        }
        return this;
      },

      /**
      * Shows all the elements by setting the css display property
      * We look to see if we were retaining an old style (like table-cell) and restore that, otherwise we set it to block
          ```
          M$().show();
          ```
      * @return {Object} an appframework object
      * @title M$().show()
      */
      show: function() {
        if (this.length === 0) {
          return this;
        }
        for (var i = 0; i < this.length; i++) {
          if (this.css('display', null, this[i]) === 'none') {
            this[i].style.display = this[i].getAttribute('mQueryOldStyle') ? this[i].getAttribute('mQueryOldStyle') : 'block';
            this[i].removeAttribute('mQueryOldStyle');
          }
        }
        return this;
      },

      /**
      * Toggle the visibility of a div
          ```
          M$().toggle();
          M$().toggle(true); //force showing
          ```
      * @param {Boolean} [show] -force the hiding or showing of the element
      * @return {Object} an appframework object
      * @title M$().toggle([show])
      */
      toggle: function(show) {
        if (this.length === 0) {
          return this;
        }
        var show2 = show === true;
        for (var i = 0; i < this.length; i++) {
          if (this.css('display', null, this[i]) !== 'none' && (show == nundefined || show2 === false)) {
            this[i].setAttribute('mQueryOldStyle', this.css('display', null, this[i]));
            this[i].style.display = 'none';
          } else if (this.css('display', null, this[i]) === 'none' && (show == nundefined || show2 === true)) {
            this[i].style.display = this[i].getAttribute('mQueryOldStyle') ? this[i].getAttribute('mQueryOldStyle') : 'block';
            this[i].removeAttribute('mQueryOldStyle');
          }
        }
        return this;
      },

      /**
      * Gets or sets an elements value
      * If used as a getter, we return the first elements value.  If nothing is in the collection, we return undefined
          ```
          M$().value; //Gets the first elements value;
          M$().value='bar'; //Sets all elements value to bar
          ```
      * @param {string=} value [optional] Value to set
      * @return {String|$mQuery} A string as a getter, appframework object as a setter
      * @title M$().val([value])
      */
      val: function(value) {
        if (this.length === 0) {
          return (value === nundefined) ? undefined : this;
        }
        if (value == nundefined)
          return this[0].value;
        for (var i = 0; i < this.length; i++) {
          this[i].value = value;
        }
        return this;
      },

      /**
      * Gets or sets an attribute on an element
      * If used as a getter, we return the first elements value.  If nothing is in the collection, we return undefined
          ```
          M$().attr('foo'); //Gets the first elements 'foo' attribute
          M$().attr('foo','bar');//Sets the elements 'foo' attribute to 'bar'
          M$().attr('foo',{bar:'bar'}) //Adds the object to an internal cache
          ```
      * @param {string|Object} attr Attribute to act upon.  If it is an object (hashmap), it will set the attributes based off the kvp.
      * @param {string|Array|Object|function|undefined} value [optional] Value to set
      * @return {string|Object|Array|Function} If used as a getter, return the attribute value.  If a setter, return an appframework object
      * @title M$().attr(attribute,[value])
      */
      attr: function(attr, value) {
        if (this.length === 0) {
          return (value === nundefined) ? undefined : this;
        }
        if (value === nundefined && !__utils.isObject(attr)) {
          var val = (this[0].mQueryCacheId && _attrCache[this[0].mQueryCacheId] && _attrCache[this[0].mQueryCacheId][attr]) ? _attrCache[this[0].mQueryCacheId][attr] : this[0].getAttribute(attr);
          return val;
        }
        for (var i = 0; i < this.length; i++) {
          if (__utils.isObject(attr)) {
            for (var key in attr) {
              $(this[i]).attr(key, attr[key]);
            }
          } else if (__utils.isArray(value) || __utils.isObject(value) || __utils.isFunction(value)) {

            if (!this[i].mQueryCacheId) {
              this[i].mQueryCacheId = __utils.uuid();
            }

            if (!_attrCache[this[i].mQueryCacheId]) {
              _attrCache[this[i].mQueryCacheId] = {};
            }
            _attrCache[this[i].mQueryCacheId][attr] = value;
          } else if (value === null) {
            this[i].removeAttribute(attr);
            if (this[i].mQueryCacheId && _attrCache[this[i].mQueryCacheId][attr]) {
              delete _attrCache[this[i].mQueryCacheId][attr];
            }
          } else {
            this[i].setAttribute(attr, value);
            if (this[i].mQueryCacheId && _attrCache[this[i].mQueryCacheId][attr]) {
              delete _attrCache[this[i].mQueryCacheId][attr];
            }
          }
        }
        return this;
      },

      /**
      * Removes one or several attribute on the elements
          ```
          M$().removeAttr('foo');
          ```
      * @param {string} attr Attributes that can be space delimited
      * @return {Object} appframework object
      * @title M$().removeAttr(attribute)
      */
      removeAttr: function(attr) {
        var removeFixer = function(param) {
          that[i].removeAttribute(param);
          if (that[i].mQueryCacheId && _attrCache[that[i].mQueryCacheId]) {
            delete _attrCache[that[i].mQueryCacheId][attr];
          }
        };
        var that = this;
        for (var i = 0; i < this.length; i++) {
          attr.split(/\s+/g).forEach(removeFixer);
        }
        return this;
      },

      /**
      * Gets or sets a property on an element
      * If used as a getter, we return the first elements value.  If nothing is in the collection, we return undefined
          ```
          M$().prop('foo'); //Gets the first elements 'foo' property
          M$().prop('foo','bar');//Sets the elements 'foo' property to 'bar'
          M$().prop('foo',{bar:'bar'}) //Adds the object to an internal cache
          ```
      * @param {string|Object} prop The property to act upon.  If it is an object (hashmap), it will set the attributes based off the kvp.
      * @param {string|Array|Object|function} [value] to set
      * @return {string|Object|Array|Function} If used as a getter, return the property value.  If a setter, return an appframework object
      * @title M$().prop(property,[value])
      */
      prop: function(prop, value) {
        if (this.length === 0) {
          return (value === nundefined) ? undefined : this;
        }
        if (value === nundefined && !__utils.isObject(prop)) {
          var res;
          var val = (this[0].mQueryCacheId && _propCache[this[0].mQueryCacheId] && _propCache[this[0].mQueryCacheId][prop]) ? _propCache[this[0].mQueryCacheId][prop] : !(res = this[0][prop]) && prop in this[0] ? this[0][prop] : res;
          return val;
        }
        for (var i = 0; i < this.length; i++) {
          if (__utils.isObject(prop)) {
            for (var key in prop) {
              $(this[i]).prop(key, prop[key]);
            }
          } else if (__utils.isArray(value) || __utils.isObject(value) || __utils.isFunction(value)) {
            if (!this[i].mQueryCacheId) {
              this[i].mQueryCacheId = __utils.uuid();
            }

            if (!_propCache[this[i].mQueryCacheId]) {
              _propCache[this[i].mQueryCacheId] = {};
            }
            _propCache[this[i].mQueryCacheId][prop] = value;
          } else if (value === null && value !== undefined) {
            $(this[i]).removeProp(prop);
          } else {
            $(this[i]).removeProp(prop);
            this[i][prop] = value;
          }
        }
        return this;
      },

      /**
      * Removes one or several properties on the elements
          ```
          M$().removeProp('foo');
          ```
      * @param {string} prop Properties that can be space delimited
      * @return {$mQuery} appframework object
      * @title M$().removeProp(attribute)
      */
      removeProp: function(prop) {
        var removePropFn = function(param) {
          try {
            if (that[i][param]) {
              that[i][param] = undefined;
            }
          } catch (e) {}

          if (that[i].mQueryCacheId && _propCache[that[i].mQueryCacheId]) {
            delete _propCache[that[i].mQueryCacheId][prop];
          }
        };
        var that = this;
        for (var i = 0; i < this.length; i++) {
          prop.split(/\s+/g).forEach(removePropFn);
        }
        return this;
      },

      /**
      * Removes elements based off a selector
          ```
              M$().remove();  //Remove all
              M$().remove('.foo');//Remove off a string selector
              var element=$('#foo').get(0);
              M$().remove(element); //Remove by an element
              M$().remove($('.foo'));  //Remove by a collection
          ```
      * @param {String|Object|Array} selector to filter against
      * @return {Object} appframework object
      * @title M$().remove(selector)
      */
      remove: function(selector) {
        var elems = $(this).filter(selector);
        if (elems == nundefined) {
          return this;
        }
        for (var i = 0; i < elems.length; i++) {
          $.cleanUpContent(elems[i], true, true);
          if (elems[i] && elems[i].parentNode) {
            elems[i].parentNode.removeChild(elems[i]);
          }
        }
        return this;
      },

      /**
      * Adds a css class to elements.
          ```
          M$().addClass('selected');
          ```
      * @param {string} name classes that are space delimited
      * @return {$mQuery} appframework object
      * @title M$().addClass(name)
      */
      addClass: function(name) {
        var addClassLoop = function(cname) {
          if (!that.hasClass(cname, that[i])) {
            classList.push(cname);
          }
        };
        if (name == nundefined) {
          return this;
        }
        for (var i = 0; i < this.length; i++) {
          var cls = this[i].className;
          var classList = [];
          var that = this;
          name.split(/\s+/g).forEach(addClassLoop);

          this[i].className += (cls ? ' ' : '') + classList.join(' ');
          this[i].className = this[i].className.trim();
        }
        return this;
      },

      /**
      * Removes a css class from elements.
          ```
          M$().removeClass('foo'); //single class
          M$().removeClass('foo selected');//remove multiple classess
          ```
      * @param {string} name classes that are space delimited
      * @return {Object} appframework object
      * @title M$().removeClass(name)
      */
      removeClass: function(name) {
        if (name == nundefined) {
          return this;
        }
        var removeClassLoop = function(cname) {
          classList = classList.replace(classRE(cname), ' ');
        };
        for (var i = 0; i < this.length; i++) {
          if (name == nundefined) {
            this[i].className = '';
            return this;
          }
          var classList = this[i].className;
          //SGV LINK EVENT
          if (typeof this[i].className === 'object') {
            classList = ' ';
          }
          name.split(/\s+/g).forEach(removeClassLoop);
          if (classList.length > 0) {
            this[i].className = classList.trim();
          } else {
            this[i].className = '';
          }
        }
        return this;
      },

      /**
      * Adds or removes one or several css classes to/from elements.
          ```
          M$().toggleClass('selected');
          ```
      * @param {string} name Classes that are space delimited
      * @param {boolean=} state [optional] Force toggle to add or remove classes
      * @return {Object} appframework object
      * @title M$().toggleClass(name)
      */
      toggleClass: function(name, state) {
        if (name == nundefined) {
          return this;
        }
        for (var i = 0; i < this.length; i++) {
          if (typeof state !== 'boolean') {
            state = this.hasClass(name, this[i]);
          }
          $(this[i])[state ? 'removeClass' : 'addClass'](name);
        }
        return this;
      },

      /**
      * Replaces a css class on elements.
          ```
          M$().replaceClass('on', 'off');
          ```
      * @param {string} name classes that are space delimited
      * @param {string} newName classes that are space delimited
      * @return {Object} appframework object
      * @title M$().replaceClass(old, new)
      */
      replaceClass: function(name, newName) {
        if (name == nundefined || newName == nundefined) {
          return this;
        }
        var replaceClassFn = function(cname) {
          classList = classList.replace(classRE(cname), ' ');
        };
        for (var i = 0; i < this.length; i++) {
          if (name == nundefined) {
            this[i].className = newName;
            continue;
          }
          var classList = this[i].className;
          name.split(/\s+/g).concat(newName.split(/\s+/g)).forEach(replaceClassFn);
          classList = classList.trim();
          if (classList.length > 0) {
            this[i].className = (classList + ' ' + newName).trim();
          } else {
            this[i].className = newName;
          }
        }
        return this;
      },

      /**
      * Checks to see if an element has a class.
          ```
          M$().hasClass('foo');
          M$().hasClass('foo',element);
          ```
      * @param {string} name Class name to check against
      * @param {Object=} element [optional] Element to check against
      * @return {boolean}
      * @title M$().hasClass(name,[element])
      */
      hasClass: function(name, element) {
        if (this.length === 0) {
          return false;
        }
        if (!element) {
          element = this[0];
        }
        return classRE(name).test(element.className);
      },

      /**
      * Appends to the elements
      * We boil everything down to an appframework object and then loop through that.
      * If it is HTML, we create a dom element so we do not break event bindings.
      * if it is a script tag, we evaluate it.
          ```
          M$().append('<div></div>'); //Creates the object from the string and appends it
          M$().append($('#foo')); //Append an object;
          ```
      *
      * @title M$().append(element, [insert], [content])
      *
      * @param {string|Object} element Element/string to add
      * @param {string|Object} content Element/string to add
      * @param {boolean=} insert [optional] insert or append
      * @return {Object} appframework object
      */
      append: function(element, content, insert) {
        if (element && element.length != nundefined && element.length === 0) {
          return this;
        }
        if (__utils.isArray(element) || __utils.isObject(element)) {
          element = $(element);
        }
        var i, node;
        if (content) {
          $(this).add(content);
        }
        for (i = 0; i < this.length; i++) {
          if (element.length && typeof element !== 'string') {
            element = $(element);
            _insertFragments(element, this[i], insert);
          } else {
            var obj = fragmentRE.test(element) ? $(element) : undefined;
            if (obj == nundefined || obj.length === 0) {
              obj = document.createTextNode(element);
            }
            if (obj instanceof $mQuery) {
              for (var k = 0, lenk = obj.length; k < lenk; k++) {
                node = obj[k];
                if (node.nodeName != nundefined && node.nodeName.toLowerCase() === 'script' && (!node.type || node.type.toLowerCase() === 'text/javascript')) {
                  window['eval'](node.innerHTML);
                } else {
                  _insertFragments($(node), this[i], insert);
                }
              }
            } else {
              insert != nundefined ? this[i].insertBefore(obj, this[i].firstChild) : this[i].appendChild(obj);
            }
          }
        }
        return this;
      },

      /**
      * Appends the current collection to the selector
          ```
          M$().appendTo('#foo'); //Append an object;
          ```
      * @param {string|Object} selector to append to
      * @title M$().appendTo(element)
      */
      appendTo: function(selector) {
        var tmp = $(selector);
        tmp.append(this);
        return this;
      },

      /**
      * Prepends the current collection to the selector
          ```
          M$().prependTo('#foo'); //Prepend an object;
          ```
      * @param {string|Object} selector Selector to prepend to
      * @return {$mQuery}
      * @title M$().prependTo(element)
      */
      prependTo: function(selector) {
        var tmp = $(selector);
        tmp.append(this, null, true);
        return this;
      },

      /**
      * Prepends to the elements
      * This simply calls append and sets insert to true
          ```
          M$().prepend('<div></div>');//Creates the object from the string and appends it
          M$().prepend($('#foo')); //Prepends an object
          ```
      * @param {Object|string} element Element/string to add
      * @return {$mQuery} appframework object
      * @title M$().prepend(element)
      */
      prepend: function(element) {
        return this.append(element, null, 1);
      },

      /**
       * Inserts collection before the target (adjacent)
          ```
          M$().insertBefore(mQuery('#target'));
          ```
       * @param {string|Object} target Target
       * @param {boolean=} after [default=false] When true, do an insert after the target
       * @return {$mQuery}
       * @title M$().insertBefore(target);
       */
      insertBefore: function(target, after) {
        if (this.length === 0) {
          return this;
        }
        target = $(target).get(0);
        if (!target) {
          return this;
        }
        for (var i = 0; i < this.length; i++) {
          after ? target.parentNode.insertBefore(this[i], target.nextSibling) : target.parentNode.insertBefore(this[i], target);
        }
        return this;
      },

      /**
       * Inserts collection after the target (adjacent)
          ```
          M$().insertAfter(mQuery('#target'));
          ```
       * @param {String|Object} target
       * @title M$().insertAfter(target);
       */
      insertAfter: function(target) {
        this.insertBefore(target, true);
      },

      /**
      * Returns the raw DOM element.
          ```
          M$().get(0); //returns the first element
          M$().get(2);// returns the third element
          ```
      * @param {Int} [index]
      * @return {Object} raw DOM element
      * @title M$().get([index])
      */
      get: function(index) {
        index = index == nundefined ? null : index;
        if (index < 0) {
          index += this.length;
        }
        if (index === null) {
          var elems = [];
          for (var i = 0; i < this.length; i++) {
            elems.push(this[i]);
          }
          return elems;
        }
        return (this[index]) ? this[index] : undefined;
      },

      /**
      * Returns the offset of the element, including traversing up the tree
          ```
          M$().offset();
          ```
      * @return {Object} with left, top, width and height properties
      * @title M$().offset()
      */
      offset: function() {
        var obj;
        if (this.length === 0) {
          return this;
        }
        if (this[0] === window) {
          return {
            left: 0,
            top: 0,
            right: 0,
            bottom: 0,
            width: window.innerWidth,
            height: window.innerHeight
          };
        } else {
          obj = this[0].getBoundingClientRect();
        }

        return {
          left: obj.left + window.pageXOffset,
          top: obj.top + window.pageYOffset,
          right: obj.right + window.pageXOffset,
          bottom: obj.bottom + window.pageYOffset,
          width: obj.right - obj.left,
          height: obj.bottom - obj.top
        };
      },

      /**
       * Returns the height of the element, including padding on IE
         ```
         M$().height();
         ```
       * @param {string=} val New Height
       * @return {number|$mQuery}
       * @title M$().height()
       */
      height: function(val) {
        if (this.length === 0) {
          return this;
        }
        if (val != nundefined) {
          return this.css('height', val);
        }
        if (this[0] === this[0].window) {
          return window.innerHeight;
        }
        if (this[0].nodeType === this[0].DOCUMENT_NODE) {
          return this[0].documentElement.offsetHeight;
        }
        else {
          var tmpVal = this.computedStyle('height').replace('px', '');
          if (tmpVal) {
            return +tmpVal;
          } else {
            return this.offset().height;
          }
        }
      },

      /**
       * Returns the width of the element, including padding on IE
         ```
         M$().width();
         ```
       * @param {string=} val New Width
       * @return {number|$mQuery}
       * @title M$().width()
       */
      width: function(val) {
        if (this.length === 0) {
          return this;
        }
        if (val != nundefined) {
          return this.css('width', val);
        }
        if (this[0] === this[0].window) {
          return window.innerWidth;
        }
        if (this[0].nodeType === this[0].DOCUMENT_NODE) {
          return this[0].documentElement.offsetWidth;
        } else {
          var tmpVal = this.computedStyle('width').replace('px', '');
          if (tmpVal) {
            return +tmpVal;
          } else {
            return this.offset().width;
          }
        }
      },

      /**
      * Returns the parent nodes of the elements based off the selector
          ```
          $('#foo').parent('.bar');
          $('#foo').parent($('.bar'));
          $('#foo').parent($('.bar').get(0));
          ``
      * @param {String|Array|Object|undefined} selector [optional]
      * @param {boolean=} recursive
      * @return {Object} appframework object with unique parents
      * @title M$().parent(selector)
      */
      parent: function(selector, recursive) {
        if (this.length === 0) {
          return this;
        }
        var elems = [];
        for (var i = 0; i < this.length; i++) {
          var tmp = this[i];
          while (tmp.parentNode && tmp.parentNode !== document) {
            elems.push(tmp.parentNode);
            if (tmp.parentNode) {
              tmp = tmp.parentNode;
            }
            if (!recursive) {
              break;
            }
          }
        }
        return this.setupOld($(unique(elems)).filter(selector));
      },

      /**
      * Returns the parents of the elements based off the selector (traversing up until html document)
          ```
          $('#foo').parents('.bar');
          $('#foo').parents($('.bar'));
          $('#foo').parents($('.bar').get(0));
          ```
      * @param {string|Array|Object|undefined} [selector]
      * @return {Object} appframework object with unique parents
      * @title M$().parents(selector)
      */
      parents: function(selector) {
        return this.parent(selector, true);
      },

      /**
      * Returns the child nodes of the elements based off the selector
          ```
          $('#foo').children('.bar'); //Selector
          $('#foo').children($('.bar')); //Objects
          $('#foo').children($('.bar').get(0)); //Single element
          ```
      * @param {string|Array|Object|undefined} selector [optional]
      * @return {Object} appframework object with unique children
      * @title M$().children(selector)
      */
      children: function(selector) {

        if (this.length === 0) {
          return this;
        }
        var elems = [];
        for (var i = 0; i < this.length; i++) {
          elems = elems.concat(siblings(this[i].firstChild));
        }
        return this.setupOld($((elems)).filter(selector));

      },

      /**
      * Returns the siblings of the element based off the selector
          ```
          $('#foo').siblings('.bar'); //Selector
          $('#foo').siblings($('.bar')); //Objects
          $('#foo').siblings($('.bar').get(0)); //Single element
          ```
      * @param {string|Array|Object|undefined} selector [optional]
      * @return {Object} appframework object with unique siblings
      * @title M$().siblings(selector)
      */
      siblings: function(selector) {
        if (this.length === 0) {
          return this;
        }
        var elems = [];
        for (var i = 0; i < this.length; i++) {
          if (this[i].parentNode) {
            elems = elems.concat(siblings(this[i].parentNode.firstChild, this[i]));
          }
        }
        return this.setupOld($(elems).filter(selector));
      },

      /**
      * Returns the child nodes of the elements based off the selector and includes text nodes
          ```
          $('#foo').contents('.bar'); //Selector
          $('#foo').contents($('.bar')); //Objects
          $('#foo').contents($('.bar').get(0)); //Single element
          ```
      * @param {string|Array|Object|undefined} selector [optional]
      * @return {Object} appframework object with unique children
      * @title M$().contents(selector)
      */
      contents: function(selector) {
        if (this.length === 0) {
          return this;
        }
        var elems = [];
        for (var i = 0; i < this.length; i++) {
          if (this[i].parentNode) {
            //elems = elems.concat(this[i].childNodes);
            _shimNodes(this[i].childNodes, elems);
          }
        }
        return this.setupOld($(elems).filter(selector));
      },

      /**
      * Returns the closest element based off the selector and optional context
          ```
          $('#foo').closest('.bar'); //Selector
          $('#foo').closest($('.bar')); //Objects
          $('#foo').closest($('.bar').get(0)); //Single element
          ```
      * @param {String|Array|Object} selector
      * @param {Object} [context]
      * @return {Object} Returns an appframework object with the closest element based off the selector
      * @title M$().closest(selector,[context]);
      */
      closest: function(selector, context) {
        if (this.length === 0) {
          return this;
        }
        var cur = this[0];

        var start = $(selector, context);
        if (start.length === 0) {
          return $();
        }
        while (cur && start.indexOf(cur) === -1) {
          cur = cur !== context && cur !== document && cur.parentNode;
        }
        return $(cur);

      },

      /**
      * Filters elements based off the selector
          ```
          $('#foo').filter('.bar'); //Selector
          $('#foo').filter($('.bar')); //Objects
          $('#foo').filter($('.bar').get(0)); //Single element
          ```
      * @param {String|Array|Object} selector
      * @return {Object} Returns an appframework object after the filter was run
      * @title M$().filter(selector);
      */
      filter: function(selector) {
        if (this.length === 0) {
          return this;
        }

        if (selector == nundefined) {
          return this;
        }
        var elems = [];
        for (var i = 0; i < this.length; i++) {
          var val = this[i];
          if (val.parentNode && $(selector, val.parentNode).indexOf(val) >= 0) {
            elems.push(val);
          }
        }
        return this.setupOld($(unique(elems)));
      },

      /**
      * Basically the reverse of filter.  Return all elements that do NOT match the selector
          ```
          $('#foo').not('.bar'); //Selector
          $('#foo').not($('.bar')); //Objects
          $('#foo').not($('.bar').get(0)); //Single element
          ```
      * @param {String|Array|Object} selector
      * @return {Object} Returns an appframework object after the filter was run
      * @title M$().not(selector);
      */
      not: function(selector) {
        if (this.length === 0) {
          return this;
        }
        var elems = [];
        for (var i = 0; i < this.length; i++) {
          var val = this[i];
          if (val.parentNode && $(selector, val.parentNode).indexOf(val) === -1) {
            elems.push(val);
          }
        }
        return this.setupOld($(unique(elems)));
      },

      /**
      * Gets or set data-* attribute parameters on elements (when a string)
      * When used as a getter, it's only the first element
          ```
          M$().data('foo'); //Gets the data-foo attribute for the first element
          M$().data('foo','bar'); //Sets the data-foo attribute for all elements
          M$().data('foo',{bar:'bar'});//object as the data
          ```
      * @param {string} key
      * @param {string|Array|Object|undefined} value [optional]
      * @return {string|Object} returns the value or appframework object
      * @title M$().data(key,[value]);
      */
      data: function(key, value) {
        return this.attr('data-' + key, value);
      },

      /**
      * Rolls back the appframework elements when filters were applied
      * This can be used after .not(), .filter(), .children(), .parent()
          ```
          M$().filter('.panel').end(); //This will return the collection BEFORE filter is applied
          ```
      * @return {Object} returns the previous appframework object before filter was applied
      * @title M$().end();
      */
      end: function() {
        return this.oldElement != nundefined ? this.oldElement : $();
      },

      /**
      * Clones the nodes in the collection.
          ```
          M$().clone();// Deep clone of all elements
          M$().clone(false); //Shallow clone
          ```
      * @param {Boolean} [deep] - do a deep copy or not
      * @return {Object} appframework object of cloned nodes
      * @title M$().clone();
      */
      clone: function(deep) {
        deep = deep === false ? false : true;
        if (this.length === 0) {
          return this;
        }
        var elems = [];
        for (var i = 0; i < this.length; i++) {
          elems.push(this[i].cloneNode(deep));
        }

        return $(elems);
      },

      /**
      * Returns the number of elements in the collection
          ```
          M$().size();
          ```
      * @return {Int}
      * @title M$().size();
      */
      size: function() {
        return this.length;
      },

      /**
       * Serializes a form into a query string
         ```
         M$().serialize();
         ```
       * @return {string}
       * @title M$().serialize()
       */
      serialize: function() {
        if (this.length === 0) {
          return '';
        }
        var serializeFn = function(elem) {
          var type = elem.getAttribute('type');
          if (elem.nodeName.toLowerCase() !== 'fieldset' && !elem.disabled && type !== 'submit' && type !== 'reset' && type !== 'button' && ((type !== 'radio' && type !== 'checkbox') || elem.checked)) {

            if (elem.getAttribute('name')) {
              if (elem.type === 'select-multiple') {
                for (var j = 0; j < elem.options.length; j++) {
                  if (elem.options[j].selected) {
                    params.push(elem.getAttribute('name') + '=' + encodeURIComponent(elem.options[j].value));
                  }
                }
              } else {
                params.push(elem.getAttribute('name') + '=' + encodeURIComponent(elem.value));
              }
            }
          }
        };
        var params = [];
        for (var i = 0; i < this.length; i++) {
          this.slice.call(this[i].elements).forEach(serializeFn);
        }
        return params.join('&');
      },

      /* added in 1.2 */
      /**
       * Reduce the set of elements based off index
          ```
         M$().eq(index)
         ```
       * @param {number} ind Index to filter by. If negative, it will go back from the end
       * @return {$mQuery} appframework object
       * @title M$().eq(index)
       */
      eq: function(ind) {
        return $(this.get(ind));
      },

      /**
       * Returns the index of the selected element in the collection
         ```
         M$().index(elem)
         ```
       * @param {string|Object} elem The element to look for. Can be a selector or object
       * @return {number} Index of selected element
       * @title M$().index(elem)
       */
      index: function(elem) {
        return elem ? this.indexOf($(elem)[0]) : this.parent().children().indexOf(this[0]);
      },

      /**
        * Returns boolean if the object is a type of the selector
        ```
        M$().is(selector)
        ```
       * @param {string|Object} selector to act upon
       * @return {boolean}
       * @title M$().is(selector)
       */
      is: function(selector) {
        return !!selector && this.filter(selector).length > 0;
      },

      /**
       * adds a result to an existing mQuery collection
       ```
       M$().add(selector)
       ```
       * @param {string|Object} selector to act upon
       * @return {$mQuery} appframework object
       * @title M$().add(selector)
       */
      add: function(selector) {
        var els = $(selector);
        var i, len = els.length;
        for (i = 0; i < len; i++) {
          this[this.length++] = els[i];
        }
        return this;
      }
    };

    /**
     * Helper function to parse the user agent.  Sets the following
     *
     * @param {Object} $
     * @param {string} userAgent
     * @api private
     */
    function detectUA($, userAgent) {
      //features
      $.feat = {};
      var head = document.documentElement.getElementsByTagName('head')[0];
      $.feat.nativeTouchScroll = typeof(head.style['-webkit-overflow-scrolling']) !== 'undefined' && (__platform.isIOS() || __platform.isBlackberry10());
      $.feat.cssPrefix = __platform.isWebkit() ? 'Webkit' : __platform.isFennec() ? 'Moz' : __platform.isIE() ? 'ms' : __platform.isOpera() ? 'O' : '';
      $.feat.cssTransformStart = !__platform.isOpera() ? '3d(' : '(';
      $.feat.cssTransformEnd = !__platform.isOpera() ? ',0)' : ')';
      
      var items = ['Webkit', 'Moz', 'ms', 'O'];
      for (var j = 0; j < items.length; j++) {
        if (document.documentElement.style[items[j] + 'Transform'] === '') {
          $.feat.cssPrefix = items[j];
        }
      }

    }

    detectUA($, navigator.userAgent);

    /**
     * Gets the css matrix, or creates a fake one
       ```
       $.getCssMatrix(domElement)
       ```

     * @param {$mQuery|string|undefined} ele
     * @return {Object} matrix with postion
     */
    $.getCssMatrix = function(ele) {
      if ($.isM$(ele)) {
        ele = ele.get(0);
      }

      var matrixFn = window.WebKitCSSMatrix || window.MSCSSMatrix;

      if (ele === nundefined) {
        if (matrixFn) {
          return new matrixFn();
        } else {
          return {
            a: 0,
            b: 0,
            c: 0,
            d: 0,
            e: 0,
            f: 0
          };
        }
      }

      var computedStyle = window.getComputedStyle(ele);

      var transform = computedStyle.webkitTransform ||
        computedStyle.transform ||
        computedStyle[$.feat.cssPrefix + 'Transform'];

      if (matrixFn) {
        return new matrixFn(transform);
      } else if (transform) {
        //fake css matrix
        var mat = transform.replace(/[^0-9\-.,]/g, '').split(',');
        return {
          a: +mat[0],
          b: +mat[1],
          c: +mat[2],
          d: +mat[3],
          e: +mat[4],
          f: +mat[5]
        };
      } else {
        return {
          a: 0,
          b: 0,
          c: 0,
          d: 0,
          e: 0,
          f: 0
        };
      }
    };

    /**
     * The following is modified from Zepto.js / events.js
     * We've removed deprecated  events like .live and allow anonymous functions to be removed
     */

    /**
     * @api private
     * @type Array.<Function>
     */
    var handlers = [],
      /**
       * @type {number}
       */
      _mQueryid = 1;

    /**
     * Gets or sets the expando property on a javascript element
     * Also increments the internal counter for elements;
     * @param {Object} element
     * @return {Int} mQueryid
     * @api private
     */
    function mQueryid(element) {
      return element._mQueryid || (element._mQueryid = _mQueryid++);
    }

    /**
     * Searches through a local array that keeps track of event handlers for proxying.
     * Since we listen for multiple events, we match up the event, function and selector.
     * This is used to find, execute, remove proxied event functions
     *
     * @param {Object} element
     * @param {string=} event
     * @param {function()} fn function [optional]
     * @param {String|Object|Array|undefined} selector [optional]
     * @return {Function|null} handler function or false if not found
     * @api private
     */
    function findHandlers(element, event, fn, selector) {
      event = parse(event);
      if (event.ns) {
        var matcher = matcherFor(event.ns);
      }
      return (handlers[mQueryid(element)] || []).filter(function(handler) {
        return handler && (!event.e || handler.e === event.e) && (!event.ns || matcher.test(handler.ns)) && (!fn || handler.fn === fn || (typeof handler.fn === 'function' && typeof fn === 'function' && handler.fn === fn)) && (!selector || handler.sel === selector);
      });
    }

    /**
     * Splits an event name by '.' to look for namespaces (e.g touch.click)
     * @param {String} event
     * @return {Object} an object with the event name and namespace
     * @api private
     */
    function parse(event) {
      var parts = ('' + event).split('.');
      return {
        e: parts[0],
        ns: parts.slice(1).sort().join(' ')
      };
    }

    /**
     * Regular expression checker for event namespace checking
     * @param {string} ns namespace
     * @return {Regex} regular expression
     * @api private
     */
    function matcherFor(ns) {
      return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)');
    }

    /**
     * Utility function that will loop through events that can be a hash or space delimited and executes the function
     * @param {String|Object} events
     * @param {Function} fn
     * @param {Iterator} [iterator]
     * @api private
     */

    function eachEvent(events, fn, iterator) {
      if (__utils.isObject(events)) {
        $.each(events, iterator);
      } else {
        events.split(/\s/).forEach(function(type) {
          iterator(type, fn);
        });
      }
    }

    /**
     * Helper function for adding an event and creating the proxy handler function.
     * All event handlers call this to wire event listeners up.  We create proxy handlers so they can be removed then.
     * This is needed for delegate/on
     * @param {Object} element
     * @param {String|Object} events
     * @param {Function} fn Function that will be executed when event triggers
     * @param {String|Array|Object|undefined} selector [optional]
     * @param {function()=} getDelegate {optional}
     * @api private
     */
    function add(element, events, fn, selector, getDelegate) {

      var id = mQueryid(element),
        set = (handlers[id] || (handlers[id] = []));
      eachEvent(events, fn, function(event, fn) {
        var delegate = getDelegate && getDelegate(fn, event),
          callback = delegate || fn;
        var proxyfn = function(event) {
          if (event.ns) {
            var matcher = matcherFor(event.ns);
            if (!matcher.test(handler.ns)) {
              return;
            }
          }

          var result = callback.apply(element, [event].concat(event.data));
          if (result === false) {
            event.preventDefault();
          }
          return result;
        };
        var handler = __utils.extend(parse(event), {
          fn: fn,
          proxy: proxyfn,
          sel: selector,
          del: delegate,
          i: set.length
        });
        set.push(handler);
        element.addEventListener(handler.e, proxyfn, false);
      });
    }

    /**
     * Helper function to remove event listeners.  We look through each event and then the proxy handler array to see if it exists
     * If found, we remove the listener and the entry from the proxy array.  If no function is specified, we remove all listeners that match
     *
     * @param {Object} element
     * @param {String|Object} events
     * @param {Function= } fn [optional]
     * @param {String|Array|Object|undefined} selector [optional]
     * @api private
     */
    function remove(element, events, fn, selector) {

      var id = mQueryid(element);
      eachEvent(events || '', fn, function(event, fn) {
        findHandlers(element, event, fn, selector).forEach(function(handler) {
          delete handlers[id][handler.i];
          element.removeEventListener(handler.e, handler.proxy, false);
        });
      });
    }


    $.event = {
      add: add,
      remove: remove
    };

    /**
    * Binds an event to each element in the collection and executes the callback
        ```
        M$().bind('click',function(){console.log('I clicked '+this.id);});
        ```
    * @param {String|Object} event
    * @param {Function} callback
    * @return {Object} appframework object
    * @title M$().bind(event,callback)
    */
    $.fn.bind = function(event, callback) {
      for (var i = 0, len = this.length; i < len; i++) {
        add(this[i], event, callback);
      }
      return this;
    };

    /**
    * Unbinds an event to each element in the collection.  If a callback is passed in, we remove just that one, otherwise we remove all callbacks for those events
        ```
        M$().unbind('click'); //Unbinds all click events
        M$().unbind('click',myFunc); //Unbinds myFunc
        ```
    * @param {string|Object} event
    * @param {Function} [callback]
    * @return {$mQuery} appframework object
    * @title M$().unbind(event,[callback]);
    */
    $.fn.unbind = function(event, callback) {
      for (var i = 0, len = this.length; i < len; i++) {
        remove(this[i], event, callback);
      }
      return this;
    };

    /**
    * Binds an event to each element in the collection that will only execute once.  When it executes, we remove the event listener then right away so it no longer happens
        ```
        M$().one('click',function(){console.log('I was clicked once');});
        ```
    * @param {string|Object} event
    * @param {Function} [callback]
    * @return appframework object
    * @title M$().one(event,callback);
    */
    $.fn.one = function(event, callback) {
      return this.each(function(i, element) {
        add(this, event, callback, null, function(fn, type) {
          return function() {
            remove(element, type, fn);
            var result = fn.apply(element, arguments);
            return result;
          };
        });
      });
    };

    /**
     * internal variables
     * @return {boolean} always returns true
     * @api private
     */
    var returnTrue = function() {
      return true;
    };

    /**
     * @return {boolean} always returns false
     */
    var returnFalse = function() {
      return false;
    };

    var eventMethods = {
      preventDefault: 'isDefaultPrevented',
      stopImmediatePropagation: 'isImmediatePropagationStopped',
      stopPropagation: 'isPropagationStopped'
    };

    /**
     * Creates a proxy function for event handlers.
     * As 'some' browsers dont support event.stopPropagation this call is bypassed if it cant be found on the event object.
     * @param {String} event
     * @return {Function} proxy
     * @api private
     */
    function createProxy(event) {
      var proxy = __utils.extend({
        originalEvent: event
      }, event);
      $.each(eventMethods, function(name, predicate) {
        proxy[name] = function() {
          this[predicate] = returnTrue;
          if (name === 'stopImmediatePropagation' || name === 'stopPropagation') {
            event.cancelBubble = true;
            if (!event[name]) {
              return;
            }
          }
          return event[name].apply(event, arguments);
        };
        proxy[predicate] = returnFalse;
      });
      return proxy;
    }

    /**
    * Delegate an event based off the selector.  The event will be registered at the parent level, but executes on the selector.
        ```
        $('#div').delegate('p','click',callback);
        ```

    * @param {string|Object} element
    * @param {String|Object} event
    * @param {Function} callback
    * @param {String|Array|Object|undefined} selector [optional]
    * @param {Object=} data [optional]
    * @return {$mQuery} appframework object
    * @title M$().delegate(selector,event,[data],callback)
    */
    function addDelegate(element, event, callback, selector, data) {
      add(element, event, callback, selector, function(fn) {
        return function(e) {
          var evt, match = $(e.target).closest(selector, element).get(0);
          if (match) {
            evt = __utils.extend(createProxy(e), {
              currentTarget: match,
              liveFired: element,
              delegateTarget: element,
              data: data
            });
            return fn.apply(match, [evt].concat([].slice.call(arguments, 1)));
          }
        };
      });
    }
    $.fn.delegate = function(selector, event, data, callback) {
      if (__utils.isFunction(data)) {
        callback = data;
        data = null;
      }
      for (var i = 0, len = this.length; i < len; i++) {
        addDelegate(this[i], event, callback, selector, data);
      }
      return this;
    };

    /**
    * Unbinds events that were registered through delegate.  It acts upon the selector and event.  If a callback is specified, it will remove that one, otherwise it removes all of them.
        ```
        $('#div').undelegate('p','click',callback);//Undelegates callback for the click event
        $('#div').undelegate('p','click');//Undelegates all click events
        ```

    * @param {String|Array|Object} selector
    * @param {String|Object} event
    * @param {Function} callback
    * @return {Object} appframework object
    * @title M$().undelegate(selector,event,[callback]);
    */
    $.fn.undelegate = function(selector, event, callback) {
      for (var i = 0, len = this.length; i < len; i++) {
        remove(this[i], event, callback, selector);
      }
      return this;
    };

    /**
    * Similar to delegate, but the function parameter order is easier to understand.
    * If selector is undefined or a function, we just call .bind, otherwise we use .delegate
        ```
        $('#div').on('click','p',callback);
        ```

    * @param {String|Array|Object} selector
    * @param {String|Object} event
    * @param {Object} data
    * @param {Function} callback
    * @return {Object} appframework object
    * @title M$().on(event,selector,[data],callback);
    */
    $.fn.on = function(event, selector, data, callback) {
      if (__utils.isFunction(data)) {
        callback = data;
        data = null;
      }

      return selector === nundefined || __utils.isFunction(selector) ? this.bind(event, selector) : this.delegate(selector, event, data, callback);
    };

    /**
    * Removes event listeners for .on()
    * If selector is undefined or a function, we call unbind, otherwise it's undelegate
        ```
        M$().off('click','p',callback); //Remove callback function for click events
        M$().off('click','p') //Remove all click events
        ```

    * @param {String|Object} event
    * @param {String|Array|Object} selector
    * @param {Function()=} [optional] callback
    * @return {Object} appframework object
    * @title M$().off(event, selector, [callback])
    */
    $.fn.off = function(event, selector, callback) {
      return selector === nundefined || __utils.isFunction(selector) ? this.unbind(event, selector) : this.undelegate(selector, event, callback);
    };

    /**
    This triggers an event to be dispatched.  Usefull for emulating events, etc.
    ```
    M$().trigger('click',{foo:'bar'});//Trigger the click event and pass in data
    ```

    * @param {string|Object} event
    * @param {Object=} data
    * @param {Object=} props
    * @return {$mQuery} appframework object
    * @title M$().trigger(event,data);
    */
    $.fn.trigger = function(event, data, props) {
      if (typeof event === 'string') {
        props = props || {};
        event = parse(event);
        props.ns = event.ns;
        event = $.Event(event.e, props);
      }
      event.data = data;
      for (var i = 0, len = this.length; i < len; i++) {
        this[i].dispatchEvent(event);
      }
      return this;
    };

    /**
     Creates a custom event to be used internally.
     ```
     $.Event('MouseEvent');
     ```
     * @api private
     * @title $.Event(type,props);
     *
     * @param {string} type
     * @param {Object=} props [properties]
     * @return {event} a custom event that can then be dispatched
     */
    $.Event = function(type, props) {
      var event = document.createEvent('Events'),
        bubbles = true;
      if (props) {
        for (var name in props) {
          (name === 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name]);
        }
      }
      event.initEvent(type, bubbles, true, null, null, null, null, null, null, null, null, null, null, null, null);
      return event;
    };

    /**
     * The following are for objects and not DOM nodes
     * @api private
     */

    /**
     * Bind an event to an object instead of a DOM Node
       ```
       $.bind(this,'event',function(){});
       ```
     * @param {Object} obj Object
     * @param {string} ev Event name
     * @param {Function} f Function to execute
     * @title $.bind(object,event,function);
     */
    $.bind = function(obj, ev, f) {
      if (!obj) {
        return;
      }
      if (!obj.__events) {
        obj.__events = {};
      }
      if (!__utils.isArray(ev)) {
        ev = [ev];
      }
      for (var i = 0; i < ev.length; i++) {
        if (!obj.__events[ev[i]]) {
          obj.__events[ev[i]] = [];
        }
        obj.__events[ev[i]].push(f);
      }
    };

    /**
     * Trigger an event to an object instead of a DOM Node
       ```
       $.trigger(this,'event',arguments);
       ```
     * @param {Object} obj object
     * @param {string} ev event name
     * @param {Array} args arguments
     * @title $.trigger(object,event,argments);
     */
    $.trigger = function(obj, ev, args) {
      if (!obj) {
        return;
      }
      var ret = true;
      if (!obj.__events) {
        return ret;
      }
      if (!__utils.isArray(ev)) {
        ev = [ev];
      }
      if (!__utils.isArray(args)) {
        args = [];
      }
      for (var i = 0; i < ev.length; i++) {
        if (obj.__events[ev[i]]) {
          var evts = obj.__events[ev[i]].slice(0);
          for (var j = 0; j < evts.length; j++) {
            if (__utils.isFunction(evts[j]) && evts[j].apply(obj, args) === false) {
              ret = false;
            }
          }
        }
      }
      return ret;
    };

    /**
     * Unbind an event to an object instead of a DOM Node
       ```
       $.unbind(this,'event',function(){});
       ```
     * @param {Object} obj Object
     * @param {String} ev Event name
     * @param {Function} f Function to execute
     * @title $.unbind(object,event,function);
     */
    $.unbind = function(obj, ev, f) {
      if (!obj.__events) {
        return;
      }
      if (ev == nundefined) {
        delete obj.__events;
        return;
      }
      if (!__utils.isArray(ev)) {
        ev = [ev];
      }
      for (var i = 0; i < ev.length; i++) {
        if (obj.__events[ev[i]]) {
          var evts = obj.__events[ev[i]];
          for (var j = 0; j < evts.length; j++) {
            if (f == nundefined) {
              delete evts[j];
            }
            if (evts[j] === f) {
              evts.splice(j, 1);
              break;
            }
          }
        }
      }
    };

    /**
     * Removes listeners on a div and its children recursively
        ```
         cleanUpNode(node,kill)
        ```
     * @param {HTMLDivElement} node the element to clean up recursively
     * @param {boolean} kill
     * @api private
     */
    function cleanUpNode(node, kill) {
      //kill it before it lays eggs!
      if (kill && node.dispatchEvent) {
        var e = $.Event('destroy', {
          bubbles: false
        });
        node.dispatchEvent(e);
      }
      //cleanup itself
      var id = mQueryid(node);
      if (id && handlers[id]) {
        for (var key in handlers[id])
          node.removeEventListener(handlers[id][key].e, handlers[id][key].proxy, false);
        delete handlers[id];
      }
    }

    function cleanUpContent(node, kill) {
      if (!node) {
        return;
      }
      //cleanup children
      var children = node.childNodes;
      if (children && children.length > 0) {
        for (var i = 0; i < children.length; i++) {
          cleanUpContent(children[i], kill);
        }
      }

      cleanUpNode(node, kill);
    }
    var cleanUpAsap = function(els, kill) {
      for (var i = 0; i < els.length; i++) {
        cleanUpContent(els[i], kill);
      }
    };

    /**
     * Function to clean up node content to prevent memory leaks
       ```
       $.cleanUpContent(node, itself, kill)
       ```
     * @param {HTMLNode} node
     * @param {boolean} itself
     * @param {boolean=} kill When set to true, this will emit a non-bubbling 'destroy' Event on the node
     * @title $.cleanUpContent(node,itself,kill)
     */
    $.cleanUpContent = function(node, itself, kill) {
      if (!node) {
        return;
      }
      //cleanup children
      var cn = node.childNodes;
      if (cn && cn.length > 0) {
        //destroy everything in a few ms to avoid memory leaks
        //remove them all and copy objs into new array
        $.asap(cleanUpAsap, {}, [slice.apply(cn, [0]), kill]);
      }
      //cleanUp this node
      if (itself) {
        cleanUpNode(node, kill);
      }
    };

    // Like setTimeout(fn, 0); but much faster
    var timeouts = [];
    var contexts = [];
    var params = [];
    /**
     * This adds a command to execute in the JS stack, but is faster then setTimeout
       ```
       $.asap(function,context,args)
       ```
     * @param {Function} fn function
     * @param {Object} context
     * @param {Array} args arguments
     */
    $.asap = function(fn, context, args) {
      if (!__utils.isFunction(fn)) {
        throw '$.asap - argument is not a valid function';
      }
      timeouts.push(fn);
      contexts.push(context ? context : {});
      params.push(args ? args : []);
      //post a message to ourselves so we know we have to execute a function from the stack
      window.postMessage('mQuery-asap', '*');
    };
    window.addEventListener('message', function(event) {
      if (event.source === window && event.data === 'mQuery-asap') {
        event.stopPropagation();
        if (timeouts.length > 0) { //just in case...
          (timeouts.shift()).apply(contexts.shift(), params.shift());
        }
      }
    }, true);

    /* custom events since people want to do M$().click instead of M$().bind('click') */
    ['click', 'keydown', 'keyup', 'keypress', 'submit', 'load', 'resize', 'change', 'select', 'error'].forEach(function(event) {
      $.fn[event] = function(cb) {
        return cb ? this.bind(event, cb) : this.trigger(event);
      };
    });


    ['focus', 'blur'].forEach(function(name) {
      $.fn[name] = function(callback) {
        if (this.length === 0) {
          return;
        }
        if (callback) {
          this.bind(name, callback);
        } else {
          for (var i = 0; i < this.length; i++) {
            try {
              this[i][name]();
            } catch (e) {}
          }
        }
        return this;
      };
    });

    /**
     * End of APIS
     * @api private
     */
    return $;

  })(window);


  module.exports = mQuery;

});

if (!Mobird.$) {
  Mobird.$ = require('m/query');
}
